// some of this code was written by <cstone@pobox.com> originally;
// it is in the public domain.

//#include <Wire.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <stdlib.h>

#include "SSD1306.h"
#include "glcdfont.c"

static uint8_t is_reversed = 0;

// a 5x7 font table
extern const uint8_t PROGMEM font[];

// the memory buffer for the LCD

static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = {
    /*--  宽度x高度=128x64 16x64=128 Gizwits--*/
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
#if (SSD1306_LCDHEIGHT == 64)
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#endif
};

void SSD1306::drawbitmap(uint8_t x, uint8_t y,
                         const uint8_t *bitmap, uint8_t w, uint8_t h,
                         uint8_t color)
{
  for (uint8_t j = 0; j < h; j++)
  {
    for (uint8_t i = 0; i < w; i++)
    {
      if (pgm_read_byte(bitmap + i + (j / 8) * w) & _BV(j % 8))
      {
        setpixel(x + i, y + j, color);
      }
    }
  }
}

void SSD1306::drawchar(uint8_t x, uint8_t line, uint8_t c)
{
  if ((line >= SSD1306_LCDHEIGHT / 8) || (x >= (SSD1306_LCDWIDTH - 6)))
    return;
  for (uint8_t i = 0; i < 5; i++)
  {
    buffer[x + (line * 128)] = pgm_read_byte(font + (c * 5) + i);
    x++;
  }
}

void SSD1306::drawstring(uint8_t offset, uint8_t line, char *c)
{
  while (c[0] != 0)
  {
    drawchar(offset, line, c[0]);
    c++;
    offset += 6; // 6 pixels wide
    if (offset + 6 >= SSD1306_LCDWIDTH)
    {
      offset = 0; // ran out of this line
      line++;
    }
    if (line >= (SSD1306_LCDHEIGHT / 8))
      return; // ran out of space :(
  }
}

// align 21*7
void SSD1306::drawstring(uint8_t a, uint8_t offset, uint8_t line, char *c)
{
  uint8_t x = 0; //left
  uint8_t len = strlen(c) * 6;
  if (a == 0)
  { //center
    x = (SSD1306_LCDWIDTH - len) / 2;
  }
  else if (a == 1) //rigth
  {
    x = SSD1306_LCDWIDTH - len - offset;
  }
  else
  {
    x = x + offset;
  }
  drawstring(x, line, c);
}

// bresenham's algorithm - thx wikpedia
void SSD1306::drawline(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
                       uint8_t color)
{
  uint8_t steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep)
  {
    swap(x0, y0);
    swap(x1, y1);
  }

  if (x0 > x1)
  {
    swap(x0, x1);
    swap(y0, y1);
  }

  uint8_t dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);

  int8_t err = dx / 2;
  int8_t ystep;

  if (y0 < y1)
  {
    ystep = 1;
  }
  else
  {
    ystep = -1;
  }

  for (; x0 < x1; x0++)
  {
    if (steep)
    {
      setpixel(y0, x0, color);
    }
    else
    {
      setpixel(x0, y0, color);
    }
    err -= dy;
    if (err < 0)
    {
      y0 += ystep;
      err += dx;
    }
  }
}

// filled rectangle
void SSD1306::fillrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
                       uint8_t color)
{

  // stupidest version - just pixels - but fast with internal buffer!
  for (uint8_t i = x; i < x + w; i++)
  {
    for (uint8_t j = y; j < y + h; j++)
    {
      setpixel(i, j, color);
    }
  }
}

// draw a rectangle
void SSD1306::drawrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
                       uint8_t color)
{
  // stupidest version - just pixels - but fast with internal buffer!
  for (uint8_t i = x; i < x + w; i++)
  {
    setpixel(i, y, color);
    setpixel(i, y + h - 1, color);
  }
  for (uint8_t i = y; i < y + h; i++)
  {
    setpixel(x, i, color);
    setpixel(x + w - 1, i, color);
  }
}

// draw a circle outline
void SSD1306::drawcircle(uint8_t x0, uint8_t y0, uint8_t r,
                         uint8_t color)
{
  int8_t f = 1 - r;
  int8_t ddF_x = 1;
  int8_t ddF_y = -2 * r;
  int8_t x = 0;
  int8_t y = r;

  setpixel(x0, y0 + r, color);
  setpixel(x0, y0 - r, color);
  setpixel(x0 + r, y0, color);
  setpixel(x0 - r, y0, color);

  while (x < y)
  {
    if (f >= 0)
    {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;

    setpixel(x0 + x, y0 + y, color);
    setpixel(x0 - x, y0 + y, color);
    setpixel(x0 + x, y0 - y, color);
    setpixel(x0 - x, y0 - y, color);

    setpixel(x0 + y, y0 + x, color);
    setpixel(x0 - y, y0 + x, color);
    setpixel(x0 + y, y0 - x, color);
    setpixel(x0 - y, y0 - x, color);
  }
}

void SSD1306::fillcircle(uint8_t x0, uint8_t y0, uint8_t r,
                         uint8_t color)
{
  int8_t f = 1 - r;
  int8_t ddF_x = 1;
  int8_t ddF_y = -2 * r;
  int8_t x = 0;
  int8_t y = r;

  for (uint8_t i = y0 - r; i <= y0 + r; i++)
  {
    setpixel(x0, i, color);
  }

  while (x < y)
  {
    if (f >= 0)
    {
      y--;
      ddF_y += 2;
      f += ddF_y;
    }
    x++;
    ddF_x += 2;
    f += ddF_x;

    for (uint8_t i = y0 - y; i <= y0 + y; i++)
    {
      setpixel(x0 + x, i, color);
      setpixel(x0 - x, i, color);
    }
    for (uint8_t i = y0 - x; i <= y0 + x; i++)
    {
      setpixel(x0 + y, i, color);
      setpixel(x0 - y, i, color);
    }
  }
}

// the most basic function, set a single pixel
void SSD1306::setpixel(uint8_t x, uint8_t y, uint8_t color)
{
  if ((x >= SSD1306_LCDWIDTH) || (y >= SSD1306_LCDHEIGHT))
    return;

  // x is which column
  if (color == WHITE)
    buffer[x + (y / 8) * SSD1306_LCDWIDTH] |= _BV((y % 8));
  else
    buffer[x + (y / 8) * SSD1306_LCDWIDTH] &= ~_BV((y % 8));
}

void SSD1306::ssd1306_init(uint8_t vccstate)
{
  // set pin directions
  pinMode(sid, OUTPUT);
  pinMode(sclk, OUTPUT);
  pinMode(dc, OUTPUT);
  pinMode(rst, OUTPUT);
  pinMode(cs, OUTPUT);

  digitalWrite(rst, HIGH);
  // VDD (3.3V) goes high at start, lets just chill for a ms
  delay(1);
  // bring0xset low
  digitalWrite(rst, LOW);
  // wait 10ms
  delay(10);
  // bring out of reset
  digitalWrite(rst, HIGH);
  // turn on VCC (9V?)

#if defined SSD1306_128_32
  // Init sequence for 128x32 OLED module
  ssd1306_command(SSD1306_DISPLAYOFF);         // 0xAE
  ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
  ssd1306_command(0x80);                       // the suggested ratio 0x80
  ssd1306_command(SSD1306_SETMULTIPLEX);       // 0xA8
  ssd1306_command(0x1F);
  ssd1306_command(SSD1306_SETDISPLAYOFFSET);   // 0xD3
  ssd1306_command(0x0);                        // no offset
  ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
  ssd1306_command(SSD1306_CHARGEPUMP);         // 0x8D
  if (vccstate == SSD1306_EXTERNALVCC)
  {
    ssd1306_command(0x10);
  }
  else
  {
    ssd1306_command(0x14);
  }
  ssd1306_command(SSD1306_MEMORYMODE); // 0x20
  ssd1306_command(0x00);               // 0x0 act like ks0108
  ssd1306_command(SSD1306_SEGREMAP | 0x1);
  ssd1306_command(SSD1306_COMSCANDEC);
  ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
  ssd1306_command(0x02);
  ssd1306_command(SSD1306_SETCONTRAST); // 0x81
  ssd1306_command(0x8F);
  ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
  if (vccstate == SSD1306_EXTERNALVCC)
  {
    ssd1306_command(0x22);
  }
  else
  {
    ssd1306_command(0xF1);
  }
  ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
  ssd1306_command(0x40);
  ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
  ssd1306_command(SSD1306_NORMALDISPLAY);       // 0xA6
#endif

#if defined SSD1306_128_64
  // Init sequence for 128x64 OLED module
  ssd1306_command(SSD1306_DISPLAYOFF);         // 0xAE
  ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
  ssd1306_command(0x80);                       // the suggested ratio 0x80
  ssd1306_command(SSD1306_SETMULTIPLEX);       // 0xA8
  ssd1306_command(0x3F);
  ssd1306_command(SSD1306_SETDISPLAYOFFSET);   // 0xD3
  ssd1306_command(0x0);                        // no offset
  ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0
  ssd1306_command(SSD1306_CHARGEPUMP);         // 0x8D
  if (vccstate == SSD1306_EXTERNALVCC)
  {
    ssd1306_command(0x10);
  }
  else
  {
    ssd1306_command(0x14);
  }
  ssd1306_command(SSD1306_MEMORYMODE); // 0x20
  ssd1306_command(0x00);               // 0x0 act like ks0108
  ssd1306_command(SSD1306_SEGREMAP | 0x1);
  ssd1306_command(SSD1306_COMSCANDEC);
  ssd1306_command(SSD1306_SETCOMPINS); // 0xDA
  ssd1306_command(0x12);
  ssd1306_command(SSD1306_SETCONTRAST); // 0x81
  if (vccstate == SSD1306_EXTERNALVCC)
  {
    ssd1306_command(0x9F);
  }
  else
  {
    ssd1306_command(0xCF);
  }
  ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9
  if (vccstate == SSD1306_EXTERNALVCC)
  {
    ssd1306_command(0x22);
  }
  else
  {
    ssd1306_command(0xF1);
  }
  ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB
  ssd1306_command(0x40);
  ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4
  ssd1306_command(SSD1306_NORMALDISPLAY);       // 0xA6
#endif

  ssd1306_command(SSD1306_DISPLAYON); //--turn on oled panel
}

void SSD1306::invert(uint8_t i)
{
  if (i)
  {
    ssd1306_command(SSD1306_INVERTDISPLAY);
  }
  else
  {
    ssd1306_command(SSD1306_NORMALDISPLAY);
  }
}

inline void SSD1306::spiwrite(uint8_t c)
{
  shiftOut(sid, sclk, MSBFIRST, c);
}
void SSD1306::ssd1306_command(uint8_t c)
{
  digitalWrite(cs, HIGH);
  digitalWrite(dc, LOW);
  digitalWrite(cs, LOW);
  spiwrite(c);
  digitalWrite(cs, HIGH);
}

void SSD1306::ssd1306_data(uint8_t c)
{
  digitalWrite(cs, HIGH);
  digitalWrite(dc, HIGH);
  digitalWrite(cs, LOW);
  spiwrite(c);
  digitalWrite(cs, HIGH);
}

void SSD1306::ssd1306_set_brightness(uint8_t val)
{
}

void SSD1306::display(void)
{
  ssd1306_command(SSD1306_SETLOWCOLUMN | 0x0);  // low col = 0
  ssd1306_command(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0
  ssd1306_command(SSD1306_SETSTARTLINE | 0x0);  // line #0

  for (uint16_t i = 0; i < (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8); i++)
  {
    ssd1306_data(buffer[i]);
  }
  // i wonder why we have to do this (check datasheet)
  if (SSD1306_LCDHEIGHT == 32)
  {
    for (uint16_t i = 0; i < (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8); i++)
    {
      ssd1306_data(0);
    }
  }
}

// clear everything
void SSD1306::clear(void)
{
  memset(buffer, 0, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
}

void SSD1306::clear(uint16_t start, uint16_t count)
{
  memset(buffer[start], 0, count);
}

void SSD1306::clear_line(uint8_t line, uint8_t n)
{
  uint16_t count = (SSD1306_LCDWIDTH / 8 * n) * 6;
  for (size_t i = 0; i < count; i++)
  {
    buffer[i + (line * 128)] = 0x00;
  }
  // memset(buffer[line * 128], 0, count);
}

void SSD1306::clear_display(void)
{
  clear();
  display();
}

// void SSD1306::buffset(uint8_t *src, uint8_t value, uint16_t size_byte)
// {
//   uint32_t align_value;
//   align_value = (uint32_t)src & 0x03;
//   if (align_value)
//   {
//     align_value = 4 - align_value;
//     if (size_byte > align_value)
//     {
//       while (align_value)
//       {
//         *(uint8_t *)src = value;
//         src += 1;
//         align_value -= 1;
//         size_byte -= 1;
//       }
//     }
//     else
//     {
//       while (size_byte)
//       {
//         *(uint8_t *)src = value;
//         src += 1;
//         size_byte -= 1;
//       }
//       return;
//     }
//   }

//   align_value = value | (value << 8);
//   align_value = align_value | (align_value << 16);
//   while (size_byte >= 4)
//   {
//     *(uint32_t *)src = align_value;
//     src += 4;
//     size_byte -= 4;
//   }
//   while (size_byte)
//   {
//     *(uint8_t *)src = value;
//     src += 1;
//     size_byte -= 1;
//   }
// }
